package com.xuzmian.demo.kotlin

import sun.security.provider.certpath.Vertex

fun defaultArguments(bar: Int = 0, baz: Int) { /*……*/
}

fun lastArgumentAfterDefaultArgument(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /*……*/
}

/**
 * 单表达式函数:当函数返回单个表达式时，可以省略花括号并且在 = 符号之后指定代码体即可：
 */
fun singleExpressionFunction(x: Int): Int = x * 2

/**
 * 当返回值类型可由编译器推断时，显式声明返回类型是可选的：
 */
fun singleExpressionFunctionOmitReturnType(x: Int) = x * 2

/**
 * 可变数量的参数:函数的参数（通常是最后一个）可以用 vararg 修饰符标记。
 * 在函数内部，类型 T 的 vararg 参数的可见方式是作为 T 数组。
 */
fun <T> variableNumberOfArguments(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}

/**
 * 中缀表示法:标有 infix 关键字的函数也可以使用中缀表示法（忽略该调用的点与圆括号）调用。中缀函数必须满足以下要求：
 * 它们必须是成员函数或扩展函数；
 * 它们必须只有一个参数；
 * 其参数不得接受可变数量的参数且不能有默认值。
 *
 * 中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符, 以下表达式是等价的：
 * 1 shl 2 + 3 与 1 shl (2 + 3)
 * 0 until n * 2 与 0 until (n * 2)
 * xs union ys as Set<*> 与 xs union (ys as Set<*>)
 *
 * 另一方面，中缀函数调用的优先级高于布尔操作符 && 与 ||、is- 与 in- 检测以及其他一些操作符。这些表达式也是等价的：
 * a && b xor c 与 a && (b xor c)
 * a xor b in c 与 (a xor b) in c
 */
infix fun Int.shl(x: Int): Int {
    return Int.MIN_VALUE
}

/**
 * 局部函数:Kotlin 支持局部函数，即一个函数在另一个函数内部：
 */
fun dfs(a: A) {
    var cs =HashSet<C>()
    fun dfs(c:C) {
        if (!cs.add(c)) return
        for (item in cs){
            if(!cs.contains(item))
            dfs(a)
        }
    }

    dfs(C())
}



/**
 * 尾递归函数:Kotlin 支持一种称为尾递归的函数式编程风格。 这允许一些通常用循环写的算法改用递归函数来写，
 * 而无堆栈溢出的风险。 当一个函数用 tailrec 修饰符标记并满足所需的形式时，编译器会优化该递归，留下一个
 * 快速而高效的基于循环的版本。
 *
 * 要符合 tailrec 修饰符的条件的话，函数必须将其自身调用作为它执行的最后一个操作。在递归调用后有更多代码时，
 * 不能使用尾递归，并且不能用在 try/catch/finally 块中。目前在 Kotlin for JVM 与 Kotlin/Native 中支持尾递归。
 */
val eps = 1E-10 // "good enough", could be 10^-15
tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))


fun functionsTest() {
    //如果一个默认参数在一个无默认值的参数之前，那么该默认值只能通过使用命名参数调用该函数来使用：
    defaultArguments(baz = 1) // 使用默认值 bar = 0

    /**
     * 如果在默认参数之后的最后一个参数是 lambda 表达式，那么它既可以作为命名参数在括号内传入，也可以在括号外传入。
     * 当一个函数调用混用位置参数与命名参数时，所有位置参数都要放在第一个命名参数之前。例如，允许调用 f(1, y = 2) 但不允许 f(x = 1, 2)。
     */
    lastArgumentAfterDefaultArgument(1) { println("hello") }     // 使用默认值 baz = 1
    lastArgumentAfterDefaultArgument(qux = { println("hello") }) // 使用两个默认值 bar = 0 与 baz = 1
    lastArgumentAfterDefaultArgument { println("hello") }        // 使用两个默认值 bar = 0 与 baz = 1

    // 用中缀表示法调用该函数
    1 shl 2
    // 等同于这样
    1.shl(2)

}
